package com.zh.kotlin_mvvm.utils.video_cache

import android.content.Context
import android.util.Log
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.net.MalformedURLException
import java.net.URL
import java.util.concurrent.ExecutorService
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadFactory
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit

/**
 * 缓存m3u8、mp4视频文件
 */
class VideoCacheUtils {
    val tag = "VideoCacheUtils"

    companion object {
        val instance by lazy { VideoCacheUtils() }
    }

    private var mThreadPool: ExecutorService? = null

    fun getThreadPool(): ExecutorService? {
        if (mThreadPool == null) {
            mThreadPool = ThreadPoolExecutor(
                50, 100, 1, TimeUnit.SECONDS,
                LinkedBlockingQueue(), DefaultThreadFactory(), ThreadPoolExecutor.DiscardPolicy()
            )
        }
        return mThreadPool
    }

    fun shutdownAll() {
        mThreadPool?.shutdownNow()
        mThreadPool = null
    }

    private class DefaultThreadFactory : ThreadFactory {
        var threadNum = 0
        override fun newThread(runnable: Runnable): Thread {
            val result = Thread(runnable, "MThreadTool-pool-thread-$threadNum")
            threadNum++
            return result
        }
    }
}

/**
 * 预缓存
 */
fun Context.preloadVideo(videoUrl: String) {
    if (!videoUrl.startsWith("http")) {
        Log.d(VideoCacheUtils.instance.tag, "videoUrl 地址错误：$videoUrl")
        return
    }
    VideoCacheUtils.instance.getThreadPool()?.execute {// 离线缓存
        val cacheFile = File(
            StorageUtils.getIndividualCacheDirectory(
                this
            ).toString() + File.separator
                    + StorageUtils.getVideoSecretFilePathByUrl(
                videoUrl
            )
        )
        if (!cacheFile.exists()) {
            cacheFile.mkdirs()
        }
        Log.d(VideoCacheUtils.instance.tag, "videoUrl 预缓存：$videoUrl")
        if (videoUrl.contains(".m3u8")) {
            downloadAndCacheM3u8(videoUrl, cacheFile)
        } else {
            downloadAndCacheMp4(videoUrl, cacheFile)
        }
    }
}

//下载mp4
private fun downloadAndCacheMp4(mp4Url: String, cacheFile: File) {
    val request = Request.Builder()
        .url(mp4Url)
        .build()
    val okHttpClient = OkHttpClient.Builder().apply {
        callTimeout(10, TimeUnit.SECONDS)
        connectTimeout(10, TimeUnit.SECONDS)
        readTimeout(10, TimeUnit.SECONDS)
        writeTimeout(10, TimeUnit.SECONDS)
    }
    okHttpClient.build().newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            // 处理失败情况
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            if (!response.isSuccessful) return
            try {
                val inputStream: InputStream? = response.body?.byteStream()
                if (inputStream != null) {
                    val cacheSize = 10 * 1024 * 1024
                    val name = if (response.body!!.contentLength() > cacheSize) ".download" else ""
                    val mp4File = File(cacheFile, Md5FileNameGenerator()
                        .generate(mp4Url) + name)
                    if (!mp4File.exists()) {
                        mp4File.createNewFile()
                    }
                    if (mp4File.length() < cacheSize) {
                        val outputStream = FileOutputStream(mp4File)
                        var length = 0
                        val buffer = ByteArray(4096)
                        var bytesRead: Int
                        while (inputStream.read(buffer)
                                .also { bytesRead = it } != -1 && length < cacheSize
                        ) {
                            outputStream.write(buffer, 0, bytesRead)
                            length += bytesRead
                        }
                        outputStream.flush()
                        outputStream.close()
                    }
                }
                inputStream?.close()
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                response.body?.close() // 触发缓存下载
            }
        }
    })
}

//解析m3u8文件、保存m3u8文件
private fun downloadAndCacheM3u8(m3u8Url: String, cacheFile: File) {
    val request = Request.Builder()
        .url(m3u8Url)
        .build()
    val okHttpClient = OkHttpClient.Builder().apply {
        callTimeout(10, TimeUnit.SECONDS)
        connectTimeout(10, TimeUnit.SECONDS)
        readTimeout(10, TimeUnit.SECONDS)
        writeTimeout(10, TimeUnit.SECONDS)
    }
    okHttpClient.build().newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            // 处理失败情况
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            if (!response.isSuccessful) return
            try {
                // 解析m3u8文件
                val inputStream: InputStream? = response.body?.byteStream()
                val reader = BufferedReader(InputStreamReader(inputStream))
                val buffer = StringBuffer()
                var index = 0
                var line: String? = reader.readLine()
                while (line != null) {
                    buffer.append(line)
                    buffer.append(System.getProperty("line.separator"))
                    if (line.startsWith("#")) {// 跳过注释行
                        if (line.contains("#EXT-X-KEY:METHOD=AES-128,URI=")) {
                            val keyUrl = line.substring(line.indexOf("URI=") + 5, line.length - 1)
                            if (keyUrl.startsWith("http")) {
                                downloadEncryptionFile(keyUrl, keyUrl, cacheFile)
                            } else {
                                val tsUrl = resolveEncryptionUrl(keyUrl, m3u8Url)
                                downloadEncryptionFile(tsUrl, keyUrl, cacheFile)
                            }
                        }
                        line = reader.readLine()
                        continue
                    } else {
                        if (index < 3) {
                            val tsUrl = resolveTsUrl(line, m3u8Url) // 解析.ts片段URL
                            downloadAndCacheTsFile(line, tsUrl, cacheFile)
                            index++
                        }
                        line = reader.readLine()
                    }
                }
                //m3u8文件地址
                val m3u8File = File(cacheFile, "index.m3u8")
                if (!m3u8File.exists()) {
                    m3u8File.createNewFile()
                }
                val outputStream = FileOutputStream(m3u8File)
                outputStream.write(buffer.toString().toByteArray())
                outputStream.flush()
                outputStream.close()
                reader.close()
                inputStream?.close()
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                response.body?.close() // 触发缓存下载
            }
        }
    })
}

//下载ts文件
private fun downloadAndCacheTsFile(tsName: String, tsUrl: String, cacheFile: File) {
    val tsRequest = Request.Builder()
        .url(tsUrl)
        .build()
    val okHttpClient = OkHttpClient.Builder().apply {
        callTimeout(10, TimeUnit.SECONDS)
        connectTimeout(10, TimeUnit.SECONDS)
        readTimeout(10, TimeUnit.SECONDS)
        writeTimeout(10, TimeUnit.SECONDS)
    }
    okHttpClient.build().newCall(tsRequest).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            // 处理失败情况
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            if (!response.isSuccessful) return
            try {
                val inputStream: InputStream? = response.body?.byteStream()
                if (inputStream != null) {
                    val tsFile =
                        File(cacheFile, if (tsName.contains("?")) tsName.split("?")[0] else tsName)
                    if (!tsFile.exists()) {
                        tsFile.createNewFile()
                    }
                    if (tsFile.length() != response.body?.contentLength()) {
                        val outputStream = FileOutputStream(tsFile)
                        val buffer = ByteArray(4096)
                        var bytesRead: Int
                        while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                            outputStream.write(buffer, 0, bytesRead)
                        }
                        outputStream.flush()
                        outputStream.close()
                    }
                }
                inputStream?.close()
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                response.body?.close() // 触发缓存下载
            }
        }
    })
}

//下载秘钥文件
private fun downloadEncryptionFile(encryptionUrl: String, keyUrl: String, cacheFile: File) {
    val tsRequest = Request.Builder()
        .url(encryptionUrl)
        .build()
    val okHttpClient = OkHttpClient.Builder().apply {
        callTimeout(10, TimeUnit.SECONDS)
        connectTimeout(10, TimeUnit.SECONDS)
        readTimeout(10, TimeUnit.SECONDS)
        writeTimeout(10, TimeUnit.SECONDS)
    }
    okHttpClient.build().newCall(tsRequest).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            // 处理失败情况
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            if (!response.isSuccessful) return
            try {
                val inputStream: InputStream? = response.body?.byteStream()
                if (inputStream != null) {
                    val encryption = if (keyUrl.contains("?"))
                        keyUrl.substring(0, keyUrl.lastIndexOf("?")) else keyUrl
                    //val key = keyUrl.substring(keyUrl.lastIndexOf("?"))
                    val encryptionFile = File(cacheFile, encryption)
                    if (!encryptionFile.exists()) {
                        encryptionFile.createNewFile()
                    }
                    if (encryptionFile.length() != response.body?.contentLength()) {
                        val outputStream = FileOutputStream(encryptionFile)
                        val buffer = ByteArray(4096)
                        var bytesRead: Int
                        while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                            outputStream.write(buffer, 0, bytesRead)
                        }
                        outputStream.flush()
                        outputStream.close()
                    }
                }
                inputStream?.close()
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                response.body?.close() // 触发缓存下载
            }
        }
    })
}

private fun resolveTsUrl(relativePath: String, baseUrl: String): String {
    return try {
        val base = URL(baseUrl)
        val url = URL(base, relativePath)
        url.toString()
    } catch (e: MalformedURLException) {
        e.printStackTrace()
        ""
    }
}

private fun resolveEncryptionUrl(encryptionPath: String, baseUrl: String): String {
    return try {
        val base = URL(baseUrl)
        val url = URL(base, encryptionPath)
        url.toString()
    } catch (e: MalformedURLException) {
        e.printStackTrace()
        ""
    }
}
